﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace C4F.DevKit.Calendar
{
    /// <summary>
    /// A class which represents a recurrence rule for an appointment.
    /// </summary>
    public class RecurrenceRule
    {
        #region Private Members

        private Frequency _frequency;

        private int _count;

        private DateTime _endDateTime;

        private int _interval;

        private List<MonthWeekday> _monthWeekdays;

        private int _monthDate;

        private List<string> _extraProperties;


        #endregion

        #region Public Constructors

        /// <summary>
        /// Creates a new recurrence rule.
        /// </summary>
        public RecurrenceRule()
        {
            MonthWeekdays = new List<MonthWeekday>();
            ExtraProperties = new List<string>();
        }

        /// <summary>
        /// Creates a new recurrence rule.
        /// </summary>
        /// <param name="freq">The frequency of the new recurrence rule.</param>
        public RecurrenceRule(Frequency freq) : this()
        {
            Frequency = freq;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The frequency of the appointment, or how often it happens.
        /// </summary>
        public Frequency Frequency
        {
            get
            {
                return _frequency;
            }
            set
            {
                _frequency = value;
            }
        }

        /// <summary>
        /// The number of times the appointment recurs. Either this or EndDateTime
        /// may be used, but not both.
        /// </summary>
        public int Count
        {
            get
            {
                return _count;
            }
            set
            {
                _count = value;
            }
        }

        /// <summary>
        /// The last date and time for the event to occur. Either this or Count may
        /// be used, but not both.
        /// </summary>
        public DateTime EndDateTime
        {
            get
            {
                return _endDateTime;
            }
            set
            {
                _endDateTime = value;
            }
        }

        /// <summary>
        /// A number indicating how often this rule repets, when combined with the frequency.
        /// </summary>
        public int Interval
        {
            get
            {
                return _interval;
            }
            set
            {
                _interval = value;
            }
        }

        /// <summary>
        /// The list of weekdays in the month on which the event recurs. This can
        /// represent recurrences like the first Saturday of the month, the second to last
        /// Thursday of the month, or every Friday.
        /// </summary>
        public List<MonthWeekday> MonthWeekdays
        {
            get
            {
                return _monthWeekdays;
            }
            set
            {
                _monthWeekdays = value;
            }
        }

        /// <summary>
        /// The date of the month on which the event recurs (for example, the first
        /// day of the month, or the third to last day of the month).
        /// </summary>
        public int MonthDate
        {
            get
            {
                return _monthDate;
            }
            set
            {
                _monthDate = value;
            }
        }

        /// <summary>
        /// The list of weekdays on which the event recurs.
        /// This property is only to be used with weekly recurrence rules.
        /// </summary>
        public List<Weekday> Weekdays
        {
            get
            {
                List<Weekday> weekdays = new List<Weekday>();

                foreach (MonthWeekday mw in MonthWeekdays)
                {
                    weekdays.Add(mw.Weekday);
                }

                return weekdays;
            }

            set
            {
                foreach (Weekday weekday in value)
                {
                    MonthWeekdays.Add(new MonthWeekday(weekday, 0));
                }
            }
        }

        #endregion

        #region Internal Properties

        internal List<string> ExtraProperties
        {
            get
            {
                return _extraProperties;
            }
            set
            {
                _extraProperties = value;
            }
        }

        #endregion
    }

    /// <summary>
    /// A type which represents a time frequency, such as minutely, hourly, daily, etc.
    /// </summary>
    public enum Frequency
    {
        /// <summary>
        /// A frequency of every second.
        /// </summary>
        Secondly,

        /// <summary>
        /// A frequency of every minute.
        /// </summary>
        Minutely,

        /// <summary>
        /// A frequency of every hour.
        /// </summary>
        Hourly,

        /// <summary>
        /// A frequency of every day.
        /// </summary>
        Daily,

        /// <summary>
        /// A frequency of every week.
        /// </summary>
        Weekly,

        /// <summary>
        /// A frequency of every month.
        /// </summary>
        Monthly,

        /// <summary>
        /// A frequency of every year.
        /// </summary>
        Yearly
    }

    /// <summary>
    /// A class which represents a certain weekday within a month, for example, the
    /// 3rd Tuesday of the month.
    /// </summary>
    public class MonthWeekday
    {
        #region Public Constructors

        /// <summary>
        /// Creates a new month weekday.
        /// </summary>
        public MonthWeekday() { }

        /// <summary>
        /// Creates a new month weekday.
        /// </summary>
        /// <param name="day">The day of the week.</param>
        /// <param name="weekNumber">The week number within the month. Use 0 for all
        /// weeks, or negative numbers for counting from the last week of the month.</param>
        public MonthWeekday(Weekday day, int weekNumber)
        {
            Weekday = day;
            WeekNumber = weekNumber;
        }

        #endregion

        #region Private Members

        private int _weekNumber;

        private Weekday _weekday;

        #endregion

        #region Public Properties

        /// <summary>
        /// The numbered week of the month in which the day occurs. When this number is 0,
        /// the day occurs on every week. Otherwise, if the number is positive, it occurs
        /// on that instance of the weekday in the month, or else if it is negative, it occurs
        /// on that instance of the weekday counted backwards from the end of the month. So
        /// 2 would mean the second instance of the weekday this month, and -1 would mean the
        /// last instance of that weekday this month.
        /// </summary>
        public int WeekNumber
        {
            get
            {
                return _weekNumber;
            }
            set
            {
                _weekNumber = value;
            }
        }

        /// <summary>
        /// The day of the week.
        /// </summary>
        public Weekday Weekday
        {
            get
            {
                return _weekday;
            }
            set
            {
                _weekday = value;
            }
        }

        #endregion
    }

    /// <summary>
    /// A type representing the days of the week.
    /// </summary>
    public enum Weekday
    {
        /// <summary>
        /// Sunday
        /// </summary>
        Sunday,

        /// <summary>
        /// Monday
        /// </summary>
        Monday,

        /// <summary>
        /// Tuesday
        /// </summary>
        Tuesday,

        /// <summary>
        /// Wednesday
        /// </summary>
        Wednesday,

        /// <summary>
        /// Thursday
        /// </summary>
        Thursday,

        /// <summary>
        /// Friday
        /// </summary>
        Friday,

        /// <summary>
        /// Saturday
        /// </summary>
        Saturday
    }
}
